# -*- coding: utf-8 -*-
"""
DTM验证工具 - 几何计算模块
负责三角剖分、面积计算、体积计算等几何运算
"""

import numpy as np
from scipy.spatial import Delaunay
from typing import List, Tuple, Union
from config_manager import Constants


class TriangulationEngine:
    """三角剖分引擎"""

    def __init__(self):
        self.constants = Constants()

    def delaunay_triangulation(self, vertices: np.ndarray) -> np.ndarray:
        """
        执行Delaunay三角剖分

        Args:
            vertices: 顶点坐标数组，形状为(n, 3)

        Returns:
            np.ndarray: 三角形数组，形状为(m, 3, 3)，每个三角形包含3个顶点

        Raises:
            ValueError: 顶点数量不足时抛出
        """
        if len(vertices) < 3:
            raise ValueError(self.constants.ERROR_MESSAGES["insufficient_vertices"])

        # 提取XY坐标进行二维三角剖分
        xy_points = vertices[:, :2]

        try:
            # 执行Delaunay三角剖分
            triangulation = Delaunay(xy_points)

            # 构建三角形顶点数组
            triangles = []
            for simplex in triangulation.simplices:
                triangle_vertices = np.array(
                    [vertices[simplex[0]], vertices[simplex[1]], vertices[simplex[2]]]
                )
                triangles.append(triangle_vertices)

            return np.array(triangles)

        except Exception as e:
            raise ValueError(f"三角剖分失败: {str(e)}")

    def validate_triangulation(self, triangles: np.ndarray) -> bool:
        """
        验证三角剖分结果的有效性

        Args:
            triangles: 三角形数组

        Returns:
            bool: 三角剖分是否有效
        """
        if triangles.size == 0:
            return False

        # 检查每个三角形是否退化（面积为0）
        for triangle in triangles:
            if self._is_degenerate_triangle(triangle):
                return False

        return True

    def _is_degenerate_triangle(self, triangle: np.ndarray) -> bool:
        """
        检查三角形是否退化（三点共线）

        Args:
            triangle: 三角形顶点数组，形状为(3, 3)

        Returns:
            bool: 是否为退化三角形
        """
        # 计算三角形面积，如果为0则为退化三角形
        area = GeometryCalculator.calculate_triangle_area_2d(triangle)
        return abs(area) < 1e-10


class GeometryCalculator:
    """几何计算器"""

    @staticmethod
    def calculate_triangle_area_2d(triangle_vertices: np.ndarray) -> float:
        """
        计算三角形在XY平面的投影面积

        Args:
            triangle_vertices: 三角形顶点坐标，形状为(3, 3)

        Returns:
            float: 投影面积
        """
        # 提取XY坐标
        v0 = triangle_vertices[0][:2]
        v1 = triangle_vertices[1][:2]
        v2 = triangle_vertices[2][:2]

        # 使用向量叉积计算面积
        vec1 = np.array(v1) - np.array(v0)
        vec2 = np.array(v2) - np.array(v0)

        # 二维叉积（行列式）
        cross_product = vec1[0] * vec2[1] - vec1[1] * vec2[0]
        return abs(cross_product) / 2.0

    @staticmethod
    def calculate_triangle_area_3d(triangle_vertices: np.ndarray) -> float:
        """
        计算三角形在三维空间的真实面积

        Args:
            triangle_vertices: 三角形顶点坐标，形状为(3, 3)

        Returns:
            float: 三维面积
        """
        v0, v1, v2 = triangle_vertices

        # 计算两个边向量
        edge1 = v1 - v0
        edge2 = v2 - v0

        # 计算叉积
        cross_product = np.cross(edge1, edge2)

        # 计算叉积的模长
        return np.linalg.norm(cross_product) / 2.0

    @staticmethod
    def calculate_triangle_volume(
        triangle_vertices: np.ndarray, base_height: float
    ) -> float:
        """
        计算三角形柱体的体积

        Args:
            triangle_vertices: 三角形顶点坐标，形状为(3, 3)
            base_height: 基准高程

        Returns:
            float: 体积（可能为负值，表示在基准面以下）
        """
        # 计算投影面积
        projected_area = GeometryCalculator.calculate_triangle_area_2d(
            triangle_vertices
        )

        # 计算平均高程
        average_height = np.mean(triangle_vertices[:, 2])

        # 计算高程差
        height_difference = average_height - base_height

        # 计算体积
        return projected_area * height_difference

    @staticmethod
    def calculate_triangle_centroid(triangle_vertices: np.ndarray) -> np.ndarray:
        """
        计算三角形的重心

        Args:
            triangle_vertices: 三角形顶点坐标，形状为(3, 3)

        Returns:
            np.ndarray: 重心坐标
        """
        return np.mean(triangle_vertices, axis=0)


class VolumeCalculator:
    """体积计算器"""

    def __init__(self):
        self.geometry_calc = GeometryCalculator()

    def calculate_total_volume(
        self, triangles: np.ndarray, base_height: float, absolute_volume: bool = True
    ) -> float:
        """
        计算所有三角形的总体积

        Args:
            triangles: 三角形数组，形状为(m, 3, 3)
            base_height: 基准高程
            absolute_volume: 是否计算绝对体积（忽略正负）

        Returns:
            float: 总体积
        """
        total_volume = 0.0

        for triangle in triangles:
            volume = self.geometry_calc.calculate_triangle_volume(triangle, base_height)

            if absolute_volume:
                total_volume += abs(volume)
            else:
                total_volume += volume

        return total_volume

    def calculate_volume_distribution(
        self, triangles: np.ndarray, base_height: float
    ) -> dict:
        """
        计算体积分布信息

        Args:
            triangles: 三角形数组
            base_height: 基准高程

        Returns:
            dict: 体积分布信息
        """
        positive_volume = 0.0  # 基准面以上的体积
        negative_volume = 0.0  # 基准面以下的体积
        volume_list = []

        for triangle in triangles:
            volume = self.geometry_calc.calculate_triangle_volume(triangle, base_height)
            volume_list.append(volume)

            if volume > 0:
                positive_volume += volume
            else:
                negative_volume += abs(volume)

        return {
            "total_absolute_volume": positive_volume + negative_volume,
            "net_volume": positive_volume - negative_volume,
            "positive_volume": positive_volume,
            "negative_volume": negative_volume,
            "volume_list": volume_list,
            "max_volume": max(volume_list) if volume_list else 0,
            "min_volume": min(volume_list) if volume_list else 0,
            "mean_volume": np.mean(volume_list) if volume_list else 0,
        }


class SurfaceCalculator:
    """表面积计算器"""

    def __init__(self):
        self.geometry_calc = GeometryCalculator()

    def calculate_total_surface_area_2d(self, triangles: np.ndarray) -> float:
        """
        计算总的XY投影面积

        Args:
            triangles: 三角形数组

        Returns:
            float: 总投影面积
        """
        total_area = 0.0

        for triangle in triangles:
            area = self.geometry_calc.calculate_triangle_area_2d(triangle)
            total_area += area

        return total_area

    def calculate_total_surface_area_3d(self, triangles: np.ndarray) -> float:
        """
        计算总的三维表面积

        Args:
            triangles: 三角形数组

        Returns:
            float: 总三维表面积
        """
        total_area = 0.0

        for triangle in triangles:
            area = self.geometry_calc.calculate_triangle_area_3d(triangle)
            total_area += area

        return total_area

    def calculate_effective_projection_area(
        self, triangles: np.ndarray, base_height: float
    ) -> float:
        """
        计算有效投影面积（仅包含基准面以上的三角形）

        Args:
            triangles: 三角形数组
            base_height: 基准高程

        Returns:
            float: 有效投影面积
        """
        effective_area = 0.0

        for triangle in triangles:
            # 检查三角形的平均高程是否高于基准面
            average_height = np.mean(triangle[:, 2])
            if average_height > base_height:
                area = self.geometry_calc.calculate_triangle_area_2d(triangle)
                effective_area += area

        return effective_area


class GeometryAnalyzer:
    """几何分析器 - 综合分析类"""

    def __init__(self):
        self.triangulation_engine = TriangulationEngine()
        self.volume_calculator = VolumeCalculator()
        self.surface_calculator = SurfaceCalculator()
        self.geometry_calc = GeometryCalculator()

    def comprehensive_analysis(self, vertices: np.ndarray, base_height: float) -> dict:
        """
        执行综合几何分析

        Args:
            vertices: 顶点坐标数组
            base_height: 基准高程

        Returns:
            dict: 综合分析结果
        """
        # 执行三角剖分
        triangles = self.triangulation_engine.delaunay_triangulation(vertices)

        # 验证三角剖分结果
        if not self.triangulation_engine.validate_triangulation(triangles):
            raise ValueError("三角剖分结果无效")

        # 计算体积信息
        volume_info = self.volume_calculator.calculate_volume_distribution(
            triangles, base_height
        )

        # 计算表面积信息
        total_3d_area = self.surface_calculator.calculate_total_surface_area_3d(
            triangles
        )
        total_2d_area = self.surface_calculator.calculate_total_surface_area_2d(
            triangles
        )
        effective_area = self.surface_calculator.calculate_effective_projection_area(
            triangles, base_height
        )

        # 统计信息
        vertex_stats = self._calculate_vertex_statistics(vertices)
        triangle_stats = self._calculate_triangle_statistics(triangles)

        return {
            "triangles": triangles,
            "triangle_count": len(triangles),
            "vertex_count": len(vertices),
            "volume_info": volume_info,
            "surface_areas": {
                "total_3d_area": total_3d_area,
                "total_projection_area": total_2d_area,
                "effective_projection_area": effective_area,
            },
            "vertex_statistics": vertex_stats,
            "triangle_statistics": triangle_stats,
            "base_height": base_height,
        }

    def _calculate_vertex_statistics(self, vertices: np.ndarray) -> dict:
        """计算顶点统计信息"""
        return {
            "x_range": (np.min(vertices[:, 0]), np.max(vertices[:, 0])),
            "y_range": (np.min(vertices[:, 1]), np.max(vertices[:, 1])),
            "z_range": (np.min(vertices[:, 2]), np.max(vertices[:, 2])),
            "centroid": np.mean(vertices, axis=0),
            "bounding_box_volume": (np.max(vertices[:, 0]) - np.min(vertices[:, 0]))
            * (np.max(vertices[:, 1]) - np.min(vertices[:, 1]))
            * (np.max(vertices[:, 2]) - np.min(vertices[:, 2])),
        }

    def _calculate_triangle_statistics(self, triangles: np.ndarray) -> dict:
        """计算三角形统计信息"""
        areas_2d = [self.geometry_calc.calculate_triangle_area_2d(t) for t in triangles]
        areas_3d = [self.geometry_calc.calculate_triangle_area_3d(t) for t in triangles]

        return {
            "min_area_2d": min(areas_2d) if areas_2d else 0,
            "max_area_2d": max(areas_2d) if areas_2d else 0,
            "mean_area_2d": np.mean(areas_2d) if areas_2d else 0,
            "min_area_3d": min(areas_3d) if areas_3d else 0,
            "max_area_3d": max(areas_3d) if areas_3d else 0,
            "mean_area_3d": np.mean(areas_3d) if areas_3d else 0,
        }
